Introduction to RStudio

RStudio is an interface that makes it easier to use R. There are four windows in RStudio. The screenshot below shows an analogy linking the different RStudio windows to cooking.

     

Working directory

Every file on your computer is located in a specific location. This location can be referred to by a path. In Mac, paths look something like this: /Users/doylemaria/Documents/. In Windows, paths look something like this: C:\Users\doylemaria\Documents\.

When you open a RStudio session, it launches from a specific location. You can find out where this is using the command getwd(). This location called the ‘working directory’. R will, by default, look in this directory when reading in data and write out files/plots to this directory. It is often useful to have your data and R Scripts in the same directory and set this as your working directory.

You can set your working directory to be anywhere you like and we will now do this:

Make a folder for this course, somewhere sensible on your computer that you will be able to easily find. Name the folder for example, Intro_R_course.

Go back to your RStudio window, go to the bottom right panel, click on the ‘Files’ tab and then click on the three dots on the top right hand corner, as shown below.

This will open up a new window which lets you explore the files and folders on your computer. Find the new folder you created (e.g. Intro_R_course), click on it, then click ‘Open’.

The files tab will now show the contents of your new folder (which should be empty). At the top of the files tab, click on More > Set As Working Directory, as shown below.

Data files

The data files required for this workshop are available on GitHub. To download the data.zip file, click on the file then click ‘Download’. Unzip the file and store this data folder in your working directory.

Tidyverse

www.tidyverse.org

www.tidyverse.org

The tidyverse is a collection of packages that includes ggplot2 and we will introduce you to some of these packages in this course.

     

Tidyverse packages

Tidyverse packages

     

The tidyverse makes data science faster, easier and more fun.

     

To install the tidyverse package you can use the install.packages function.

install.packages('tidyverse')

RNA-seq dataset

In this tutorial, we will learn some R through creating plots to visualise data from an RNA-seq experiment. We will create some plots using published RNA-seq data from the paper by Fu et al. 2015. This study examined expression in basal and luminal cells from mice at different stages (virgin, pregnant and lactating). There are 2 samples per group and 6 groups, 12 samples in total. In this first session we will generate some plots to explore the data, to see if it looks ok.

We will use the raw counts here. These are the counts of reads for each gene for each sample. The higher the number of counts the more the gene is expressed.    

Loading the data

First let’s open a new R script. From the top menu in RStudio: File > New File > R Script. Let’s save it as intro.R.

We will begin by loading in the packages that we need.

library(tidyverse)

library() is the function in R that we use to load packages. We will see many functions in the course. Functions are “canned scripts” that automate more complicated sets of commands. Many functions are predefined, or can be made available by importing R packages. A function usually takes one or more inputs called arguments. Here tidyverse is the argument to the library() function. Note that functions require parentheses after the function name.

The file we will use is a csv comma-separated file, so we will use the read_csv() function from the tidyverse readr package to read it in. Tsv stands for There is also a read_tsv() function for tab-separated values.

To see what the read_csv() function (or any function in R) does, type a ? before the name and the help will appear in the Help panel on the right in RStudio. Or you can search the function name in the Help panel search box.

?read_csv

We will use the counts file called GSE60450_GeneLevel_Normalized(CPM.and.TMM)_data.csv that’s in a folder called data i.e. the path to the file should be data/GSE60450_GeneLevel_Normalized(CPM.and.TMM)_data.csv.

We can read the counts file into R with the command below. We’ll store the contents of the counts file in an object called counts. This stores the file contents in R’s memory making it easier to use.

# read in counts file
counts <- read_csv("data/GSE60450_GeneLevel_Normalized(CPM.and.TMM)_data.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  gene_symbol = col_character(),
  GSM1480291 = col_double(),
  GSM1480292 = col_double(),
  GSM1480293 = col_double(),
  GSM1480294 = col_double(),
  GSM1480295 = col_double(),
  GSM1480296 = col_double(),
  GSM1480297 = col_double(),
  GSM1480298 = col_double(),
  GSM1480299 = col_double(),
  GSM1480300 = col_double(),
  GSM1480301 = col_double(),
  GSM1480302 = col_double()
)
# read in metadata
sampleinfo <- read_csv("data/GSE60450_filtered_metadata.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  characteristics = col_character(),
  immunophenotype = col_character(),
  `developmental stage` = col_character()
)

In R we use <- to assign values to objects. <- is the assignment operator. It assigns values on the right to objects on the left. So to create an object, we need to give it a name (e.g. counts), followed by the assignment operator <-, and the value we want to give it. We can give an object almost any name we want but there are some rules and conventions as described here

We can read in a file from a path on our computer on on the web and use this as the value. Note that we need to put quotes (“”) around file paths.

Assignment operator shortcut

In RStudio, typing Alt + - (push Alt at the same time as the - key) will write <- in a single keystroke in a PC, while typing > Option + - (push Option at the same time as the - key) does the same in a Mac.

The value of counts is the contents of the counts file. There is some information output by read_tsv on column specifications, this is the data type that read_tsv has guessed is contained in each column, we will discuss this more later.

Getting to know the data

When assigning a value to an object, R does not print the value. For example, here we don’t see what’s in the counts or sampleinfo files. But there are ways we can look at the data. We will demonstrate using the sampleinfo object.

We can type the name of the object and this will print the first few lines and some information, such as number of rows.

sampleinfo

We can also use dim() to see the dimensions of an object, the number of rows and columns.

dim(sampleinfo)
[1] 12  4

This show us there are 12 rows and 4 columns.

In the Environment Tab in the top right panel in RStudio we can also see the number of rows and columns in the objects we have in our session.

We can also take a look the first few lines with head(). This shows us the first 6 lines.

head(sampleinfo)

We can look at the last few lines with tail(). This shows us the last 6 lines. This can be useful to check the bottom of the file, that it looks ok.

tail(sampleinfo)

Or we can see the whole file with View().

View(sampleinfo)

In the Environment tab we can see how many rows and columns the object contains and we can click on the icon to view all the contents in a tab. This runs the command View() for us.

We can see all the column names with colnames().

colnames(sampleinfo)
[1] "X1"                  "characteristics"     "immunophenotype"     "developmental stage"

We can access individual columns by name using the $ symbol. For example we can see what’s contained in column X1.

sampleinfo$X1
 [1] "GSM1480291" "GSM1480292" "GSM1480293" "GSM1480294" "GSM1480295" "GSM1480296" "GSM1480297"
 [8] "GSM1480298" "GSM1480299" "GSM1480300" "GSM1480301" "GSM1480302"

If we just wanted to see the first 3 values in the column we can specify this using square brackets.

sampleinfo$X1[1:3]
[1] "GSM1480291" "GSM1480292" "GSM1480293"

Other useful commands for checking data are str() and summary().

str() shows us the structure of our data. It shows us what columns there are, the first few entries, and what data type they are e.g. character or numbers (double or integer).

str(sampleinfo)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   12 obs. of  4 variables:
 $ X1                 : chr  "GSM1480291" "GSM1480292" "GSM1480293" "GSM1480294" ...
 $ characteristics    : chr  "mammary gland, luminal cells, virgin" "mammary gland, luminal cells, virgin" "mammary gland, luminal cells, 18.5 day pregnancy" "mammary gland, luminal cells, 18.5 day pregnancy" ...
 $ immunophenotype    : chr  "luminal cell population" "luminal cell population" "luminal cell population" "luminal cell population" ...
 $ developmental stage: chr  "virgin" "virgin" "18.5 day pregnancy" "18.5 day pregnancy" ...
 - attr(*, "spec")=
  .. cols(
  ..   X1 = col_character(),
  ..   characteristics = col_character(),
  ..   immunophenotype = col_character(),
  ..   `developmental stage` = col_character()
  .. )

summary() generates summary statistics of our data. For numeric columns (columns of type double or integer) it outputs statistics such as the min, max, mean and median. We will demonstrate this with the counts file as it contains numeric data. For character columns it shows us the length (how many rows).

summary(counts)
      X1            gene_symbol          GSM1480291          GSM1480292       
 Length:23735       Length:23735       Min.   :    0.000   Min.   :    0.000  
 Class :character   Class :character   1st Qu.:    0.000   1st Qu.:    0.000  
 Mode  :character   Mode  :character   Median :    1.745   Median :    1.891  
                                       Mean   :   42.132   Mean   :   42.132  
                                       3rd Qu.:   29.840   3rd Qu.:   29.604  
                                       Max.   :12525.066   Max.   :12416.211  
   GSM1480293         GSM1480294         GSM1480295          GSM1480296       
 Min.   :    0.00   Min.   :    0.00   Min.   :     0.00   Min.   :     0.00  
 1st Qu.:    0.00   1st Qu.:    0.00   1st Qu.:     0.00   1st Qu.:     0.00  
 Median :    0.92   Median :    0.89   Median :     0.58   Median :     0.54  
 Mean   :   42.13   Mean   :   42.13   Mean   :    42.13   Mean   :    42.13  
 3rd Qu.:   21.91   3rd Qu.:   19.92   3rd Qu.:    12.27   3rd Qu.:    12.28  
 Max.   :49191.15   Max.   :55692.09   Max.   :111850.87   Max.   :108726.08  
   GSM1480297          GSM1480298          GSM1480299          GSM1480300       
 Min.   :    0.000   Min.   :    0.000   Min.   :    0.000   Min.   :    0.000  
 1st Qu.:    0.000   1st Qu.:    0.000   1st Qu.:    0.000   1st Qu.:    0.000  
 Median :    2.158   Median :    2.254   Median :    1.854   Median :    1.816  
 Mean   :   42.132   Mean   :   42.132   Mean   :   42.132   Mean   :   42.132  
 3rd Qu.:   27.414   3rd Qu.:   26.450   3rd Qu.:   24.860   3rd Qu.:   23.443  
 Max.   :10489.311   Max.   :10662.486   Max.   :15194.048   Max.   :17434.935  
   GSM1480301          GSM1480302       
 Min.   :    0.000   Min.   :    0.000  
 1st Qu.:    0.000   1st Qu.:    0.000  
 Median :    1.629   Median :    1.749  
 Mean   :   42.132   Mean   :   42.132  
 3rd Qu.:   23.443   3rd Qu.:   24.818  
 Max.   :19152.728   Max.   :15997.193  

Formatting the data

We will first convert the data into tidy format to make it easier to work with.

seqdata <- gather(counts, key=Sample, value=Count, starts_with("GSM"))

Let’s have a look at the data.

seqdata
allinfo <- full_join(seqdata, sampleinfo, by = c("Sample" = "X1"))

Let’s have a look at the data.

allinfo

Plotting with ggplot2

ggplot2 is a plotting package that makes it simple to create complex plots. One really great benefit of ggplot2 versus the older base R plotting is that we only need to make minimal changes if the underlying data change or if we decide to change our plot type, for example, from a bar plot to a box plot. This helps in creating publication quality plots with minimal amounts of adjustments and tweaking.

ggplot2 likes data in the ‘long’ format, also called ‘tidy’ format, i.e., a column for every variable, and a row for every observation. Well-structured data will save you lots of time when making figures with ggplot2. We will discuss tidy data more later in the course.

ggplot graphics are built step by step by adding new elements. Adding layers in this fashion allows for extensive flexibility and customization of plots.

To build a ggplot, we use the following basic template that can be used for different types of plots. Three things are required for a ggplot:

  1. The data
  2. The mapping of variables (columns) in the data to visual properties (called aesthetics in ggplot2) of objects in the plot
  3. The type of plot – this is called a geom in ggplot2 terminology

There are different geoms we can use to create different types of plot e.g. bar plot versus box plot, to see some of the geoms available see the ggplot2 help or the handy ggplot2 cheatsheet.

We can make boxplots to visualise the distribution of the counts for each sample. This helps us to compare the samples and check if any look unusual.

ggplot(data=allinfo, mapping=aes(x=Sample, y=Count)) + 
  geom_boxplot()

We have generated our first plot!

But it looks a bit weird. It’s because we have some genes with extremely high counts. To make it easier to visualise the distributions we usually plot the logarithm of RNA-seq counts. We’ll plot the Sample on the X axis and log2 Counts on the y axis. We can log the Counts within the aes(). (The sample labels are also overlapping each other, we will show how to fix this later)

ggplot(data=allinfo, mapping=aes(x=Sample, y=log2(Count))) + 
  geom_boxplot()

Box plots are useful summaries, but hide the shape of the distribution. For example, if the distribution is bimodal, we would not see it in a boxplot. An alternative to the boxplot is the violin plot, where the shape (of the density of points) is drawn. See here for an example of how differences in distribution may be hidden in box plots but revealed with violin plots. We could also make jitter plots. A jitter plot is similar to a scatter plot. It adds a small amount of random variation to the location of each point so they don’t overlap. There are too many points in this case for the jitter plots to be useful but this is just to demonstrate, as jitter with and without boxplot is a commonly used ggplot type. We will also make use of jitter plots later.

Exercise

You can easily make different types of plots with ggplotby using different geoms. If you type “geom” in RStudio, RStudio will show you the different types of geoms you can use. Using the same data (same x and y values)

  1. Make a violin plot (geom_violin)
  2. Make a jitter plot (geom_jitter)
  3. Make a boxplot with a jitter plot overlaid (Hint: you can add multiple geoms with + )

What if we would like to add some colour to the plot, for example, a different colour bar for each sample.

If we look at the geom_boxplot help we can see under the heading called “Aesthetics” that there’s an option for colour. Let’s try adding that to our plot. We’ll specify we want to map the Sample column to colour=. As we are mapping colour to a column in our data we need to put this inside the aes().

ggplot(data=allinfo, mapping=aes(x=Sample, y=log2(Count), colour=Sample)) + 
  geom_boxplot()

Hmm colouring the edges wasn’t quite what we had in mind. Look at the help for geom_boxplot to see what other aesthetic we could use. Let’s try fill= instead.

ggplot(data=allinfo, mapping=aes(x=Sample, y=log2(Count), fill=Sample)) + 
  geom_boxplot()

That looks better. fill= is used to fill in areas in ggplot2 plots, whereas colour= is used to colour lines and points.

A really nice feature about ggplot is that we can easily colour by another variable e.g. cell type (basal vs luminal) by simply changing the column we give to fill=.

Exercise

Colour the plots by other variables (columns) in the metadata file:

  1. characteristics
  2. immunophenotype
  3. developmental stage (Check what happens if you don’t use backticks)

Specifying colours

We might want to change the colours. To see what colour names are available you can type colours(). Note that here we see the function c() for the first time. We use function extremely often in R when we have multiple items that we are combining. Here we have two colours we want to use, so we need to use c() to combine them to give to values=.

mycolours <- c("turquoise", "plum", "snow", "mistyrose", "lemonchiffon", "chocolate")

Then we give these colours to + scale_fill_manual(values=mycolours).

ggplot(data=allinfo, mapping=aes(x=Sample, y=log2(Count), fill=characteristics)) + 
  geom_boxplot() +
  scale_fill_manual(values=mycolours)

There are built-in colour palettes that can be handy to use, where the sets of colours are predefined. scale_fill_brewer() is a popular one (there is also scale_colour_brewer()). You can take a look at the help for scale_fill_brewer() to see what pallettes are available. There is also an R colours cheatsheet that shows the colours of the palettes here. There’s one called “Dark2”, let’s have a look at that.

ggplot(data=allinfo, mapping=aes(x=Sample, y=log2(Count), fill=characteristics)) + 
  geom_boxplot() +
  scale_fill_brewer(palette = "Dark2")

Exercise

Make a colourblind friendly plot. Hint there are colourblind friendly palettes here

Make subplots for each gene

With ggplot we can easily make subplots using faceting. For example we can make stripcharts, plotting expression by groups for each gene. First we’ll use mutate to add a column with shorter group names to use in the plot as the group names in the characteristics column are quite long.

 allinfo <- mutate(allinfo, Group=case_when(                                        
        str_detect(characteristics, "basal.*virgin") ~ "bvirg",
        str_detect(characteristics, "basal.*preg")  ~ "bpreg",
        str_detect(characteristics, "basal.*lact")  ~ "blact",
        str_detect(characteristics, "luminal.*virgin")  ~ "lvirg",
        str_detect(characteristics, "luminal.*preg")  ~ "lpreg",
        str_detect(characteristics, "luminal.*lact")  ~ "llact"
       ))

We can make plots for a set of genes.

mygenes <- c("Acta2", "COX1", "Csn1s1", "Csn1s2a", "Csn2", "Csn3")

These are the top 6 genes with highest count across all samples that can be obtained with group_by and summarise mygenes <- allinfo %>% group_by(gene_symbol) %>% summarise(Total_count=sum(Count)) %>% top_n(6, Total_count)%>% pull(gene_symbol)

We filter our data for just these genes of interest.

mygenes_counts <- filter(allinfo, gene_symbol %in% mygenes)

We can make boxplots for just these genes. We facet on the gene_symbol column.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, fill=Group)) +
  geom_boxplot() +
  facet_wrap(~gene_symbol)

We only have two values per group so we could just plot the individual points. We could use geom_point to make a scatterplot.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count)) +
  geom_point() +
  facet_wrap(~gene_symbol)

The points are overlapping so we will use geom_jitter which adds a small amount of random variation.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count)) +
  geom_jitter() +
  facet_wrap(~gene_symbol)

We can colour the groups similar to before using colour=.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) 

Modifying the plot

Axis labels and Title

We can change the axis labels and add a title with labs(). To change the x axis label we use labs(x="New name"). To change the y axis label we use labs(y="New name") or we can change them all at the same time.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) + 
  labs(x="Cell type and stage", y="Count", title="Mammary gland RNA-seq data")

Themes

We can adjust the text on the x axis (the group labels by turning them 90 degrees so we can read the labels better.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) + 
  labs(x="Cell type and stage", y="Count", title="Mammary gland RNA-seq data") +
  theme(axis.text.x = element_text(angle = 90))

We can remove the grey background and grid lines. To do this we modify the ggplot theme. Themes are the non-data parts of the plot.

There are also a lot of built-in themes. Let’s have a look at a couple of the more widely used themes. We won’t save these (we won’t use p <-) we’ll just print them to have a look. The default ggplot theme is theme_grey().

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) + 
  labs(x="Cell type and stage", y="Count", title="Mammary gland RNA-seq data") +
  theme(axis.text.x = element_text(angle = 90)) + 
  theme_bw()

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) + 
  labs(x="Cell type and stage", y="Count", title="Mammary gland RNA-seq data") +
  theme(axis.text.x = element_text(angle = 90)) + 
  theme_minimal()

There are many themes available, you can see some in the R graph gallery.

We can also modify parts of the theme individually. We can remove the grey background and grid lines with the code below.

ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) + 
  labs(x="Cell type and stage", y="Count", title="Mammary gland RNA-seq data") +
  theme(axis.text.x = element_text(angle = 90)) + 
  theme(panel.background = element_blank(), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank())

Saving plots

We can save plots interactively by clicking Export in the Plots window. Or we can output plots to pdf using pdf() followed by dev.off(). We put our plot code after the call to pdf() and before closing the plot device with dev.off().

Let’s save our last plot.

pdf("myplot.pdf")
ggplot(data=mygenes_counts, mapping=aes(x=Group, y=Count, colour=Group)) +
  geom_jitter() +
  facet_wrap(~gene_symbol) + 
  labs(x="Cell type and stage", y="Count", title="Mammary gland RNA-seq data") +
  theme(axis.text.x = element_text(angle = 90)) + 
  theme(panel.background = element_blank(), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank())
dev.off()

Key Points

  • We use the library() function to load packages we want to use. Note that they need to be already installed with e.g. install.packages().
  • We can read in csv files with read_csv().
  • ggplot2 is a plotting package that makes it simple to create complex plots and have 3 components: data (dataset), mapping (columns to plot) and geom (type of plot).
  • The + sign is used to add geoms (and new layers as we will see later in the course). It must be placed at the end of the preceding line. If it is added at the beginning of the line, ggplot2 will return an error message.
  • ggplot2 plots can be easily coloured by columns in the dataset. fill= can be used to colour areas.
  • A pdf of the plot can be generated with pdf() and dev.off()
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgZm9yIEJpb2xvZ2lzdHMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB5ZXMKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIgJVknKWAiCi0tLQoKIyBJbnRyb2R1Y3Rpb24gdG8gUlN0dWRpbwoKUlN0dWRpbyBpcyBhbiBpbnRlcmZhY2UgdGhhdCBtYWtlcyBpdCBlYXNpZXIgdG8gdXNlIFIuIFRoZXJlIGFyZSBmb3VyIHdpbmRvd3MgaW4gUlN0dWRpby4gVGhlIHNjcmVlbnNob3QgYmVsb3cgc2hvd3MgYW4gW2FuYWxvZ3kgbGlua2luZyB0aGUgZGlmZmVyZW50IFJTdHVkaW8gd2luZG93cyB0byBjb29raW5nXShodHRwczovL3R3aXR0ZXIuY29tL1JMYWRpZXNOQ0wvc3RhdHVzLzExMzg4MTI4MjY5MTc3MjQxNjApLgoKIVtdKGltYWdlcy9yc3R1ZGlvX2Nvb2tpbmcuanBnKQoKXCAgClwgIApcICAKCgojIyBXb3JraW5nIGRpcmVjdG9yeQpFdmVyeSBmaWxlIG9uIHlvdXIgY29tcHV0ZXIgaXMgbG9jYXRlZCBpbiBhIHNwZWNpZmljIGxvY2F0aW9uLiBUaGlzIGxvY2F0aW9uIGNhbiBiZSByZWZlcnJlZCB0byBieSBhIHBhdGguIEluIE1hYywgcGF0aHMgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOiBgL1VzZXJzL2RveWxlbWFyaWEvRG9jdW1lbnRzL2AuIEluIFdpbmRvd3MsIHBhdGhzIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczogYEM6XFVzZXJzXGRveWxlbWFyaWFcRG9jdW1lbnRzXGAuCgpXaGVuIHlvdSBvcGVuIGEgUlN0dWRpbyBzZXNzaW9uLCBpdCBsYXVuY2hlcyBmcm9tIGEgc3BlY2lmaWMgbG9jYXRpb24uIFlvdSBjYW4gZmluZCBvdXQgd2hlcmUgdGhpcyBpcyB1c2luZyB0aGUgY29tbWFuZCBgZ2V0d2QoKWAuIFRoaXMgbG9jYXRpb24gY2FsbGVkIHRoZSDigJh3b3JraW5nIGRpcmVjdG9yeeKAmS4gUiB3aWxsLCBieSBkZWZhdWx0LCBsb29rIGluIHRoaXMgZGlyZWN0b3J5IHdoZW4gcmVhZGluZyBpbiBkYXRhIGFuZCB3cml0ZSBvdXQgZmlsZXMvcGxvdHMgdG8gdGhpcyBkaXJlY3RvcnkuIEl0IGlzIG9mdGVuIHVzZWZ1bCB0byBoYXZlIHlvdXIgZGF0YSBhbmQgUiBTY3JpcHRzIGluIHRoZSBzYW1lIGRpcmVjdG9yeSBhbmQgc2V0IHRoaXMgYXMgeW91ciB3b3JraW5nIGRpcmVjdG9yeS4KCllvdSBjYW4gc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgdG8gYmUgYW55d2hlcmUgeW91IGxpa2UgYW5kIHdlIHdpbGwgbm93IGRvIHRoaXM6CgpNYWtlIGEgZm9sZGVyIGZvciB0aGlzIGNvdXJzZSwgc29tZXdoZXJlIHNlbnNpYmxlIG9uIHlvdXIgY29tcHV0ZXIgdGhhdCB5b3Ugd2lsbCBiZSBhYmxlIHRvIGVhc2lseSBmaW5kLiBOYW1lIHRoZSBmb2xkZXIgZm9yIGV4YW1wbGUsIGBJbnRyb19SX2NvdXJzZWAuCgpHbyBiYWNrIHRvIHlvdXIgUlN0dWRpbyB3aW5kb3csIGdvIHRvIHRoZSBib3R0b20gcmlnaHQgcGFuZWwsIGNsaWNrIG9uIHRoZSDigJhGaWxlc+KAmSB0YWIgYW5kIHRoZW4gY2xpY2sgb24gdGhlIHRocmVlIGRvdHMgb24gdGhlIHRvcCByaWdodCBoYW5kIGNvcm5lciwgYXMgc2hvd24gYmVsb3cuCgohW10oaW1hZ2VzL3RocmVlX2RvdHMucG5nKQoKVGhpcyB3aWxsIG9wZW4gdXAgYSBuZXcgd2luZG93IHdoaWNoIGxldHMgeW91IGV4cGxvcmUgdGhlIGZpbGVzIGFuZCBmb2xkZXJzIG9uIHlvdXIgY29tcHV0ZXIuIEZpbmQgdGhlIG5ldyBmb2xkZXIgeW91IGNyZWF0ZWQgKGUuZy4gYEludHJvX1JfY291cnNlYCksIGNsaWNrIG9uIGl0LCB0aGVuIGNsaWNrIOKAmE9wZW7igJkuCgpUaGUgZmlsZXMgdGFiIHdpbGwgbm93IHNob3cgdGhlIGNvbnRlbnRzIG9mIHlvdXIgbmV3IGZvbGRlciAod2hpY2ggc2hvdWxkIGJlIGVtcHR5KS4gQXQgdGhlIHRvcCBvZiB0aGUgZmlsZXMgdGFiLCBjbGljayBvbiBgTW9yZSA+IFNldCBBcyBXb3JraW5nIERpcmVjdG9yeWAsIGFzIHNob3duIGJlbG93LgoKIVtdKGltYWdlcy93b3JraW5nX2RpcmVjdG9yeS5wbmcpCgoKIyMjIERhdGEgZmlsZXMKVGhlIGRhdGEgZmlsZXMgcmVxdWlyZWQgZm9yIHRoaXMgd29ya3Nob3AgYXJlIGF2YWlsYWJsZSBvbiBbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vUE1hY0RhU2NpL3ItaW50cm8tYmlvbG9naXN0cy90cmVlL21hc3Rlci9kYXRhKS4gVG8gZG93bmxvYWQgdGhlIGRhdGEuemlwIGZpbGUsIGNsaWNrIG9uIHRoZSBmaWxlIHRoZW4gY2xpY2sgJ0Rvd25sb2FkJy4gVW56aXAgdGhlIGZpbGUgYW5kIHN0b3JlIHRoaXMgYGRhdGFgIGZvbGRlciBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LgoKCiMjIyBUaWR5dmVyc2UKCiFbd3d3LnRpZHl2ZXJzZS5vcmddKGltYWdlcy90aWR5dmVyc2UucG5nKXt3aWR0aD0xMDAlIH0KClRoZSAqKnRpZHl2ZXJzZSoqIGlzIGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyB0aGF0IGluY2x1ZGVzICAqKmBnZ3Bsb3QyYCoqIGFuZCB3ZSB3aWxsIGludHJvZHVjZSB5b3UgdG8gc29tZSBvZiB0aGVzZSBwYWNrYWdlcyBpbiB0aGlzIGNvdXJzZS4KClwgIApcICAKXCAgCgohW1RpZHl2ZXJzZSBwYWNrYWdlc10oaW1hZ2VzL3RpZHl2ZXJzZV9wYWNrYWdlcy5qcGVnKXt3aWR0aD01MCUgfQoKXCAgClwgIApcIAoKKipUaGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pIG1ha2VzIGRhdGEgc2NpZW5jZSBmYXN0ZXIsIGVhc2llciBhbmQgbW9yZSBmdW4uKioKClwgIApcICAKXCAgCgpUbyBpbnN0YWxsIHRoZSB0aWR5dmVyc2UgcGFja2FnZSB5b3UgY2FuIHVzZSB0aGUgYGluc3RhbGwucGFja2FnZXNgIGZ1bmN0aW9uLgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpCmBgYAoKCiMjIyBSTkEtc2VxIGRhdGFzZXQKSW4gdGhpcyB0dXRvcmlhbCwgd2Ugd2lsbCBsZWFybiBzb21lIFIgdGhyb3VnaCBjcmVhdGluZyBwbG90cyB0byB2aXN1YWxpc2UgZGF0YSBmcm9tIGFuIFJOQS1zZXEgZXhwZXJpbWVudC4gV2Ugd2lsbCBjcmVhdGUgc29tZSBwbG90cyB1c2luZyBwdWJsaXNoZWQgUk5BLXNlcSBkYXRhIGZyb20gdGhlIHBhcGVyIGJ5IFtGdSBldCBhbC4gMjAxNV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMjU3MzA0NzIpLiBUaGlzIHN0dWR5IGV4YW1pbmVkIGV4cHJlc3Npb24gaW4gYmFzYWwgYW5kIGx1bWluYWwgY2VsbHMgZnJvbSBtaWNlIGF0IGRpZmZlcmVudCBzdGFnZXMgKHZpcmdpbiwgcHJlZ25hbnQgYW5kIGxhY3RhdGluZykuIFRoZXJlIGFyZSAyIHNhbXBsZXMgcGVyIGdyb3VwIGFuZCA2IGdyb3VwcywgMTIgc2FtcGxlcyBpbiB0b3RhbC4gSW4gdGhpcyBmaXJzdCBzZXNzaW9uIHdlIHdpbGwgZ2VuZXJhdGUgc29tZSBwbG90cyB0byBleHBsb3JlIHRoZSBkYXRhLCB0byBzZWUgaWYgaXQgbG9va3Mgb2suCgohW10oaW1hZ2VzL21vdXNlX2V4cC5wbmcpCgpXZSB3aWxsIHVzZSB0aGUgcmF3IGNvdW50cyBoZXJlLiBUaGVzZSBhcmUgdGhlIGNvdW50cyBvZiByZWFkcyBmb3IgZWFjaCBnZW5lIGZvciBlYWNoIHNhbXBsZS4gVGhlIGhpZ2hlciB0aGUgbnVtYmVyIG9mIGNvdW50cyB0aGUgbW9yZSB0aGUgZ2VuZSBpcyBleHByZXNzZWQuClwgIApcICAKCiMjIExvYWRpbmcgdGhlIGRhdGEKCkZpcnN0IGxldCdzIG9wZW4gYSBuZXcgUiBzY3JpcHQuIEZyb20gdGhlIHRvcCBtZW51IGluIFJTdHVkaW86IGBGaWxlID4gTmV3IEZpbGUgPiBSIFNjcmlwdGAuCkxldCdzIHNhdmUgaXQgYXMgYGludHJvLlJgLgoKV2Ugd2lsbCBiZWdpbiBieSBsb2FkaW5nIGluIHRoZSBwYWNrYWdlcyB0aGF0IHdlIG5lZWQuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKYGxpYnJhcnkoKWAgaXMgdGhlICoqZnVuY3Rpb24qKiBpbiBSIHRoYXQgd2UgdXNlIHRvIGxvYWQgcGFja2FnZXMuIFdlIHdpbGwgc2VlIG1hbnkgZnVuY3Rpb25zIGluIHRoZSBjb3Vyc2UuIEZ1bmN0aW9ucyBhcmUgImNhbm5lZCBzY3JpcHRzIiB0aGF0IGF1dG9tYXRlIG1vcmUgY29tcGxpY2F0ZWQgc2V0cyBvZiBjb21tYW5kcy4gTWFueSBmdW5jdGlvbnMgYXJlIHByZWRlZmluZWQsIG9yIGNhbiBiZSBtYWRlIGF2YWlsYWJsZSBieSBpbXBvcnRpbmcgUiAqcGFja2FnZXMqLiBBIGZ1bmN0aW9uIHVzdWFsbHkgdGFrZXMgb25lIG9yIG1vcmUgaW5wdXRzIGNhbGxlZCAqYXJndW1lbnRzKi4gSGVyZSB0aWR5dmVyc2UgaXMgdGhlIGFyZ3VtZW50IHRvIHRoZSBgbGlicmFyeSgpYCBmdW5jdGlvbi4gTm90ZSB0aGF0IGZ1bmN0aW9ucyByZXF1aXJlIHBhcmVudGhlc2VzIGFmdGVyIHRoZSBmdW5jdGlvbiBuYW1lLgoKVGhlIGZpbGUgd2Ugd2lsbCB1c2UgaXMgYSBjc3YgY29tbWEtc2VwYXJhdGVkIGZpbGUsIHNvIHdlIHdpbGwgdXNlIHRoZSBgcmVhZF9jc3YoKWAgZnVuY3Rpb24gZnJvbSB0aGUgdGlkeXZlcnNlICoqYHJlYWRyYCoqIHBhY2thZ2UgdG8gcmVhZCBpdCBpbi4gVHN2IHN0YW5kcyBmb3IgIFRoZXJlIGlzIGFsc28gYSBgcmVhZF90c3YoKWAgZnVuY3Rpb24gZm9yIHRhYi1zZXBhcmF0ZWQgdmFsdWVzLgoKVG8gc2VlIHdoYXQgdGhlIGByZWFkX2NzdigpYCBmdW5jdGlvbiAob3IgYW55IGZ1bmN0aW9uIGluIFIpIGRvZXMsIHR5cGUgYSBgP2AgYmVmb3JlIHRoZSBuYW1lIGFuZCB0aGUgaGVscCB3aWxsIGFwcGVhciBpbiB0aGUgSGVscCBwYW5lbCBvbiB0aGUgcmlnaHQgaW4gUlN0dWRpby4gT3IgeW91IGNhbiBzZWFyY2ggdGhlIGZ1bmN0aW9uIG5hbWUgaW4gdGhlIEhlbHAgcGFuZWwgc2VhcmNoIGJveC4KCmBgYHtyLCBldmFsPUZBTFNFfQo/cmVhZF9jc3YKYGBgCgpXZSB3aWxsIHVzZSB0aGUgY291bnRzIGZpbGUgY2FsbGVkIGBHU0U2MDQ1MF9HZW5lTGV2ZWxfTm9ybWFsaXplZChDUE0uYW5kLlRNTSlfZGF0YS5jc3ZgIHRoYXQncyBpbiBhIGZvbGRlciBjYWxsZWQgYGRhdGFgIGkuZS4gdGhlIHBhdGggdG8gdGhlIGZpbGUgc2hvdWxkIGJlIGBkYXRhL0dTRTYwNDUwX0dlbmVMZXZlbF9Ob3JtYWxpemVkKENQTS5hbmQuVE1NKV9kYXRhLmNzdmAuCgpXZSBjYW4gcmVhZCB0aGUgY291bnRzIGZpbGUgaW50byBSIHdpdGggdGhlIGNvbW1hbmQgYmVsb3cuIFdlJ2xsIHN0b3JlIHRoZSBjb250ZW50cyBvZiB0aGUgY291bnRzIGZpbGUgaW4gYW4gKipvYmplY3QqKiBjYWxsZWQgYGNvdW50c2AuIFRoaXMgc3RvcmVzIHRoZSBmaWxlIGNvbnRlbnRzIGluIFIncyBtZW1vcnkgbWFraW5nIGl0IGVhc2llciB0byB1c2UuCgpgYGB7cn0KIyByZWFkIGluIGNvdW50cyBmaWxlCmNvdW50cyA8LSByZWFkX2NzdigiZGF0YS9HU0U2MDQ1MF9HZW5lTGV2ZWxfTm9ybWFsaXplZChDUE0uYW5kLlRNTSlfZGF0YS5jc3YiKQoKIyByZWFkIGluIG1ldGFkYXRhCnNhbXBsZWluZm8gPC0gcmVhZF9jc3YoImRhdGEvR1NFNjA0NTBfZmlsdGVyZWRfbWV0YWRhdGEuY3N2IikKCmBgYAoKSW4gUiB3ZSB1c2UgYDwtYCB0byBhc3NpZ24gdmFsdWVzIHRvIG9iamVjdHMuIGA8LWAgaXMgdGhlICoqYXNzaWdubWVudCBvcGVyYXRvcioqLiAgSXQgYXNzaWducyB2YWx1ZXMgb24gdGhlIHJpZ2h0IHRvIG9iamVjdHMgb24gdGhlIGxlZnQuIFNvIHRvIGNyZWF0ZSBhbiBvYmplY3QsIHdlIG5lZWQgdG8gZ2l2ZSBpdCBhIG5hbWUgKGUuZy4gYGNvdW50c2ApLCBmb2xsb3dlZCBieSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciBgPC1gLCBhbmQgdGhlIHZhbHVlIHdlIHdhbnQgdG8gZ2l2ZSBpdC4gV2UgY2FuIGdpdmUgYW4gb2JqZWN0IGFsbW9zdCBhbnkgbmFtZSB3ZSB3YW50IGJ1dCB0aGVyZSBhcmUgc29tZSBydWxlcyBhbmQgY29udmVudGlvbnMgYXMgZGVzY3JpYmVkIFtoZXJlXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL1ItZ2Vub21pY3MvMDEtaW50cm8tdG8tUi5odG1sI25vdGVzX29uX29iamVjdHMpCgpXZSBjYW4gcmVhZCBpbiBhIGZpbGUgZnJvbSBhIHBhdGggb24gb3VyIGNvbXB1dGVyIG9uIG9uIHRoZSB3ZWIgYW5kIHVzZSB0aGlzIGFzIHRoZSB2YWx1ZS4gTm90ZSB0aGF0IHdlIG5lZWQgdG8gcHV0IHF1b3RlcyAoIiIpIGFyb3VuZCBmaWxlIHBhdGhzLgoKPiAjIyMjIEFzc2lnbm1lbnQgb3BlcmF0b3Igc2hvcnRjdXQKPiBJbiBSU3R1ZGlvLCB0eXBpbmcgPGtiZD5BbHQ8L2tiZD4gKyA8a2JkPi08L2tiZD4gKHB1c2ggPGtiZD5BbHQ8L2tiZD4gYXQgdGhlCj4gc2FtZSB0aW1lIGFzIHRoZSA8a2JkPi08L2tiZD4ga2V5KSB3aWxsIHdyaXRlIGAgPC0gYCBpbiBhIHNpbmdsZSBrZXlzdHJva2UgaW4gYSBQQywgd2hpbGUgdHlwaW5nID4gPGtiZD5PcHRpb248L2tiZD4gKyA8a2JkPi08L2tiZD4gKHB1c2ggPGtiZD5PcHRpb248L2tiZD4gYXQgdGhlCj4gc2FtZSB0aW1lIGFzIHRoZSA8a2JkPi08L2tiZD4ga2V5KSBkb2VzIHRoZSBzYW1lIGluIGEgTWFjLiAKClRoZSB2YWx1ZSBvZiBgY291bnRzYCBpcyB0aGUgY29udGVudHMgb2YgdGhlIGNvdW50cyBmaWxlLiBUaGVyZSBpcyBzb21lIGluZm9ybWF0aW9uIG91dHB1dCBieSBgcmVhZF90c3ZgIG9uIGNvbHVtbiBzcGVjaWZpY2F0aW9ucywgdGhpcyBpcyB0aGUgZGF0YSB0eXBlIHRoYXQgYHJlYWRfdHN2YCBoYXMgZ3Vlc3NlZCBpcyBjb250YWluZWQgaW4gZWFjaCBjb2x1bW4sIHdlIHdpbGwgZGlzY3VzcyB0aGlzIG1vcmUgbGF0ZXIuIAoKIyMgR2V0dGluZyB0byBrbm93IHRoZSBkYXRhCgpXaGVuIGFzc2lnbmluZyBhIHZhbHVlIHRvIGFuIG9iamVjdCwgUiBkb2VzIG5vdCBwcmludCB0aGUgdmFsdWUuIEZvciBleGFtcGxlLCBoZXJlIHdlIGRvbid0IHNlZSB3aGF0J3MgaW4gdGhlIGNvdW50cyBvciBzYW1wbGVpbmZvIGZpbGVzLiBCdXQgdGhlcmUgYXJlIHdheXMgd2UgY2FuIGxvb2sgYXQgdGhlIGRhdGEuIFdlIHdpbGwgZGVtb25zdHJhdGUgdXNpbmcgdGhlIGBzYW1wbGVpbmZvYCBvYmplY3QuCgpXZSBjYW4gdHlwZSB0aGUgbmFtZSBvZiB0aGUgb2JqZWN0IGFuZCB0aGlzIHdpbGwgcHJpbnQgdGhlIGZpcnN0IGZldyBsaW5lcyBhbmQgc29tZSBpbmZvcm1hdGlvbiwgc3VjaCBhcyBudW1iZXIgb2Ygcm93cy4gCgpgYGB7cn0Kc2FtcGxlaW5mbwpgYGAKCldlIGNhbiBhbHNvIHVzZSBgZGltKClgIHRvIHNlZSB0aGUgZGltZW5zaW9ucyBvZiBhbiBvYmplY3QsIHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucy4KCmBgYHtyfQpkaW0oc2FtcGxlaW5mbykKYGBgCgpUaGlzIHNob3cgdXMgdGhlcmUgYXJlIDEyIHJvd3MgYW5kIDQgY29sdW1ucy4KCkluIHRoZSBFbnZpcm9ubWVudCBUYWIgaW4gdGhlIHRvcCByaWdodCBwYW5lbCBpbiBSU3R1ZGlvIHdlIGNhbiBhbHNvIHNlZSB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMgaW4gdGhlIG9iamVjdHMgd2UgaGF2ZSBpbiBvdXIgc2Vzc2lvbi4KCldlIGNhbiBhbHNvIHRha2UgYSBsb29rIHRoZSBmaXJzdCBmZXcgbGluZXMgd2l0aCBgaGVhZCgpYC4gVGhpcyBzaG93cyB1cyB0aGUgZmlyc3QgNiBsaW5lcy4KCmBgYHtyfQpoZWFkKHNhbXBsZWluZm8pCmBgYAoKV2UgY2FuIGxvb2sgYXQgdGhlIGxhc3QgZmV3IGxpbmVzIHdpdGggYHRhaWwoKWAuIFRoaXMgc2hvd3MgdXMgdGhlIGxhc3QgNiBsaW5lcy4gVGhpcyBjYW4gYmUgdXNlZnVsIHRvIGNoZWNrIHRoZSBib3R0b20gb2YgdGhlIGZpbGUsIHRoYXQgaXQgbG9va3Mgb2suCmBgYHtyfQp0YWlsKHNhbXBsZWluZm8pCmBgYAoKT3Igd2UgY2FuIHNlZSB0aGUgd2hvbGUgZmlsZSB3aXRoIGBWaWV3KClgLgoKYGBge3IgZXZhbD1GQUxTRX0KVmlldyhzYW1wbGVpbmZvKQpgYGAKCkluIHRoZSBFbnZpcm9ubWVudCB0YWIgd2UgY2FuIHNlZSBob3cgbWFueSByb3dzIGFuZCBjb2x1bW5zIHRoZSBvYmplY3QgY29udGFpbnMgYW5kIHdlIGNhbiBjbGljayBvbiB0aGUgaWNvbiB0byB2aWV3IGFsbCB0aGUgY29udGVudHMgaW4gYSB0YWIuIFRoaXMgcnVucyB0aGUgY29tbWFuZCBWaWV3KCkgZm9yIHVzLgoKV2UgY2FuIHNlZSBhbGwgdGhlIGNvbHVtbiBuYW1lcyB3aXRoIGBjb2xuYW1lcygpYC4KYGBge3J9CmNvbG5hbWVzKHNhbXBsZWluZm8pCmBgYAoKV2UgY2FuIGFjY2VzcyBpbmRpdmlkdWFsIGNvbHVtbnMgYnkgbmFtZSB1c2luZyB0aGUgYCRgIHN5bWJvbC4gRm9yIGV4YW1wbGUgd2UgY2FuIHNlZSB3aGF0J3MgY29udGFpbmVkIGluIGNvbHVtbiBYMS4KCmBgYHtyfQpzYW1wbGVpbmZvJFgxCmBgYAoKSWYgd2UganVzdCB3YW50ZWQgdG8gc2VlIHRoZSBmaXJzdCAzIHZhbHVlcyBpbiB0aGUgY29sdW1uIHdlIGNhbiBzcGVjaWZ5IHRoaXMgdXNpbmcgc3F1YXJlIGJyYWNrZXRzLgoKYGBge3J9CnNhbXBsZWluZm8kWDFbMTozXQpgYGAKCk90aGVyIHVzZWZ1bCBjb21tYW5kcyBmb3IgY2hlY2tpbmcgZGF0YSBhcmUgYHN0cigpYCBhbmQgYHN1bW1hcnkoKWAuCgpgc3RyKClgIHNob3dzIHVzIHRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIGRhdGEuIEl0IHNob3dzIHVzIHdoYXQgY29sdW1ucyB0aGVyZSBhcmUsIHRoZSBmaXJzdCBmZXcgZW50cmllcywgYW5kIHdoYXQgZGF0YSB0eXBlIHRoZXkgYXJlIGUuZy4gY2hhcmFjdGVyIG9yIG51bWJlcnMgKGRvdWJsZSBvciBpbnRlZ2VyKS4KCmBgYHtyfQpzdHIoc2FtcGxlaW5mbykKYGBgCgpgc3VtbWFyeSgpYCBnZW5lcmF0ZXMgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIG91ciBkYXRhLiBGb3IgbnVtZXJpYyBjb2x1bW5zIChjb2x1bW5zIG9mIHR5cGUgZG91YmxlIG9yIGludGVnZXIpIGl0IG91dHB1dHMgc3RhdGlzdGljcyBzdWNoIGFzIHRoZSBtaW4sIG1heCwgbWVhbiBhbmQgbWVkaWFuLiBXZSB3aWxsIGRlbW9uc3RyYXRlIHRoaXMgd2l0aCB0aGUgY291bnRzIGZpbGUgYXMgaXQgY29udGFpbnMgbnVtZXJpYyBkYXRhLiBGb3IgY2hhcmFjdGVyIGNvbHVtbnMgaXQgc2hvd3MgdXMgdGhlIGxlbmd0aCAoaG93IG1hbnkgcm93cykuCgpgYGB7cn0Kc3VtbWFyeShjb3VudHMpCmBgYAoKCiMgRm9ybWF0dGluZyB0aGUgZGF0YQoKV2Ugd2lsbCBmaXJzdCBjb252ZXJ0IHRoZSBkYXRhIGludG8gdGlkeSBmb3JtYXQgdG8gbWFrZSBpdCBlYXNpZXIgdG8gd29yayB3aXRoLgoKIVtdKGltYWdlcy9yZXNoYXBlX2RhdGEucG5nKQoKYGBge3J9CnNlcWRhdGEgPC0gZ2F0aGVyKGNvdW50cywga2V5PVNhbXBsZSwgdmFsdWU9Q291bnQsIHN0YXJ0c193aXRoKCJHU00iKSkKYGBgCgpMZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgZGF0YS4KYGBge3J9CnNlcWRhdGEKYGBgCgohW10oaW1hZ2VzL2pvaW5fZGF0YS5wbmcpCgpgYGB7cn0KYWxsaW5mbyA8LSBmdWxsX2pvaW4oc2VxZGF0YSwgc2FtcGxlaW5mbywgYnkgPSBjKCJTYW1wbGUiID0gIlgxIikpCmBgYAoKTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIGRhdGEuCmBgYHtyfQphbGxpbmZvCmBgYAoKIyBQbG90dGluZyB3aXRoICoqYGdncGxvdDJgKioKCioqYGdncGxvdDJgKiogaXMgYSBwbG90dGluZyBwYWNrYWdlIHRoYXQgbWFrZXMgaXQgc2ltcGxlIHRvIGNyZWF0ZSBjb21wbGV4IHBsb3RzLiBPbmUgcmVhbGx5IGdyZWF0IGJlbmVmaXQgb2YgZ2dwbG90MiB2ZXJzdXMgdGhlIG9sZGVyIGJhc2UgUiBwbG90dGluZyBpcyB0aGF0IHdlIG9ubHkgbmVlZCB0byBtYWtlIG1pbmltYWwgY2hhbmdlcyBpZiB0aGUgdW5kZXJseWluZyBkYXRhIGNoYW5nZSBvciBpZiB3ZSBkZWNpZGUgdG8gY2hhbmdlIG91ciBwbG90IHR5cGUsIGZvciBleGFtcGxlLCBmcm9tIGEgYmFyIHBsb3QgdG8gYSBib3ggcGxvdC4gVGhpcyBoZWxwcyBpbiBjcmVhdGluZyBwdWJsaWNhdGlvbiBxdWFsaXR5IHBsb3RzIHdpdGggbWluaW1hbCBhbW91bnRzIG9mIGFkanVzdG1lbnRzIGFuZCB0d2Vha2luZy4KCioqYGdncGxvdDJgKiogbGlrZXMgZGF0YSBpbiB0aGUgJ2xvbmcnIGZvcm1hdCwgYWxzbyBjYWxsZWQgJ3RpZHknIGZvcm1hdCwgaS5lLiwgYSBjb2x1bW4gZm9yIGV2ZXJ5IHZhcmlhYmxlLCBhbmQgYSByb3cgZm9yIGV2ZXJ5IG9ic2VydmF0aW9uLiBXZWxsLXN0cnVjdHVyZWQgZGF0YSB3aWxsIHNhdmUgeW91IGxvdHMgb2YgdGltZQp3aGVuIG1ha2luZyBmaWd1cmVzIHdpdGggKipgZ2dwbG90MmAqKi4gV2Ugd2lsbCBkaXNjdXNzIHRpZHkgZGF0YSBtb3JlIGxhdGVyIGluIHRoZSBjb3Vyc2UuCgpnZ3Bsb3QgZ3JhcGhpY3MgYXJlIGJ1aWx0IHN0ZXAgYnkgc3RlcCBieSBhZGRpbmcgbmV3IGVsZW1lbnRzLiBBZGRpbmcgbGF5ZXJzIGluCnRoaXMgZmFzaGlvbiBhbGxvd3MgZm9yIGV4dGVuc2l2ZSBmbGV4aWJpbGl0eSBhbmQgY3VzdG9taXphdGlvbiBvZiBwbG90cy4KClRvIGJ1aWxkIGEgZ2dwbG90LCB3ZSB1c2UgdGhlIGZvbGxvd2luZyBiYXNpYyB0ZW1wbGF0ZSB0aGF0IGNhbiBiZSB1c2VkIGZvciBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMuIFRocmVlIHRoaW5ncyBhcmUgcmVxdWlyZWQgZm9yIGEgZ2dwbG90OgoKIVtdKGltYWdlcy9nZ3Bsb3RfdGVtcGxhdGUucG5nKQoKMS4gVGhlIGRhdGEKMi4gVGhlIG1hcHBpbmcgb2YgdmFyaWFibGVzIChjb2x1bW5zKSBpbiB0aGUgZGF0YSB0byB2aXN1YWwgcHJvcGVydGllcyAoY2FsbGVkIGFlc3RoZXRpY3MgaW4gZ2dwbG90Mikgb2Ygb2JqZWN0cyBpbiB0aGUgcGxvdAozLiBUaGUgdHlwZSBvZiBwbG90IOKAkyB0aGlzIGlzIGNhbGxlZCBhIGdlb20gaW4gZ2dwbG90MiB0ZXJtaW5vbG9neQoKVGhlcmUgYXJlIGRpZmZlcmVudCBnZW9tcyB3ZSBjYW4gdXNlIHRvIGNyZWF0ZSBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdCBlLmcuIGJhciBwbG90IHZlcnN1cyBib3ggcGxvdCwgdG8gc2VlIHNvbWUgb2YgdGhlIGdlb21zIGF2YWlsYWJsZSBzZWUgdGhlIGdncGxvdDIgaGVscCBvciB0aGUgaGFuZHkgW2dncGxvdDIgY2hlYXRzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9kYXRhLXZpc3VhbGl6YXRpb24tMi4xLnBkZikuCgpXZSBjYW4gbWFrZSBib3hwbG90cyB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgY291bnRzIGZvciBlYWNoIHNhbXBsZS4gVGhpcyBoZWxwcyB1cyB0byBjb21wYXJlIHRoZSBzYW1wbGVzIGFuZCBjaGVjayBpZiBhbnkgbG9vayB1bnVzdWFsLgoKYGBge3J9CmdncGxvdChkYXRhPWFsbGluZm8sIG1hcHBpbmc9YWVzKHg9U2FtcGxlLCB5PUNvdW50KSkgKyAKICBnZW9tX2JveHBsb3QoKQpgYGAKCldlIGhhdmUgZ2VuZXJhdGVkIG91ciBmaXJzdCBwbG90IQoKQnV0IGl0IGxvb2tzIGEgYml0IHdlaXJkLiBJdCdzIGJlY2F1c2Ugd2UgaGF2ZSBzb21lIGdlbmVzIHdpdGggZXh0cmVtZWx5IGhpZ2ggY291bnRzLiBUbyBtYWtlIGl0IGVhc2llciB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbnMgd2UgdXN1YWxseSBwbG90IHRoZSBsb2dhcml0aG0gb2YgUk5BLXNlcSBjb3VudHMuIFdlJ2xsIHBsb3QgdGhlIFNhbXBsZSBvbiB0aGUgWCBheGlzIGFuZCBsb2d+Mn4gQ291bnRzIG9uIHRoZSB5IGF4aXMuIFdlIGNhbiBsb2cgdGhlIENvdW50cyB3aXRoaW4gdGhlIGBhZXMoKWAuIChUaGUgc2FtcGxlIGxhYmVscyBhcmUgYWxzbyBvdmVybGFwcGluZyBlYWNoIG90aGVyLCB3ZSB3aWxsIHNob3cgaG93IHRvIGZpeCB0aGlzIGxhdGVyKQoKYGBge3J9CmdncGxvdChkYXRhPWFsbGluZm8sIG1hcHBpbmc9YWVzKHg9U2FtcGxlLCB5PWxvZzIoQ291bnQpKSkgKyAKICBnZW9tX2JveHBsb3QoKQpgYGAKCkJveCBwbG90cyBhcmUgdXNlZnVsIHN1bW1hcmllcywgYnV0IGhpZGUgdGhlIHNoYXBlIG9mIHRoZSBkaXN0cmlidXRpb24uIEZvciBleGFtcGxlLCBpZiB0aGUgZGlzdHJpYnV0aW9uIGlzIGJpbW9kYWwsIHdlIHdvdWxkIG5vdCBzZWUgaXQgaW4gYSBib3hwbG90LiBBbiBhbHRlcm5hdGl2ZSB0byB0aGUgYm94cGxvdCBpcyB0aGUgKip2aW9saW4gcGxvdCoqLCB3aGVyZSB0aGUgc2hhcGUgKG9mIHRoZSBkZW5zaXR5IG9mIHBvaW50cykgaXMgZHJhd24uIFNlZSBoZXJlIGZvciBhbiBleGFtcGxlIG9mIGhvdyBkaWZmZXJlbmNlcyBpbiBkaXN0cmlidXRpb24gbWF5IGJlIGhpZGRlbiBpbiBib3ggcGxvdHMgYnV0IHJldmVhbGVkIHdpdGggdmlvbGluIHBsb3RzLiBXZSBjb3VsZCBhbHNvIG1ha2Ugaml0dGVyIHBsb3RzLiBBICoqaml0dGVyIHBsb3QqKiBpcyBzaW1pbGFyIHRvIGEgc2NhdHRlciBwbG90LiBJdCBhZGRzIGEgc21hbGwgYW1vdW50IG9mIHJhbmRvbSB2YXJpYXRpb24gdG8gdGhlIGxvY2F0aW9uIG9mIGVhY2ggcG9pbnQgc28gdGhleSBkb27igJl0IG92ZXJsYXAuIFRoZXJlIGFyZSB0b28gbWFueSBwb2ludHMgaW4gdGhpcyBjYXNlIGZvciB0aGUgaml0dGVyIHBsb3RzIHRvIGJlIHVzZWZ1bCBidXQgdGhpcyBpcyBqdXN0IHRvIGRlbW9uc3RyYXRlLCBhcyBbaml0dGVyIHdpdGggYW5kIHdpdGhvdXQgYm94cGxvdF0oaHR0cHM6Ly9zaW1wbHlzdGF0aXN0aWNzLm9yZy8yMDE5LzAyLzIxL2R5bmFtaXRlLXBsb3RzLW11c3QtZGllLykgaXMgYSBjb21tb25seSB1c2VkIGdncGxvdCB0eXBlLiBXZSB3aWxsIGFsc28gbWFrZSB1c2Ugb2Ygaml0dGVyIHBsb3RzIGxhdGVyLgoKIyMjIyBFeGVyY2lzZSAKWW91IGNhbiBlYXNpbHkgbWFrZSBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMgd2l0aCBnZ3Bsb3RieSB1c2luZyBkaWZmZXJlbnQgZ2VvbXMuIElmIHlvdSB0eXBlICJnZW9tIiBpbiBSU3R1ZGlvLCBSU3R1ZGlvIHdpbGwgc2hvdyB5b3UgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBnZW9tcyB5b3UgY2FuIHVzZS4gVXNpbmcgdGhlIHNhbWUgZGF0YSAoc2FtZSB4IGFuZCB5IHZhbHVlcykKCjEuIE1ha2UgYSB2aW9saW4gcGxvdCAoZ2VvbV92aW9saW4pCjIuIE1ha2UgYSBqaXR0ZXIgcGxvdCAoZ2VvbV9qaXR0ZXIpCjMuIE1ha2UgYSBib3hwbG90IHdpdGggYSBqaXR0ZXIgcGxvdCBvdmVybGFpZCAoSGludDogeW91IGNhbiBhZGQgbXVsdGlwbGUgZ2VvbXMgd2l0aCArICkKCldoYXQgaWYgd2Ugd291bGQgbGlrZSB0byBhZGQgc29tZSBjb2xvdXIgdG8gdGhlIHBsb3QsIGZvciBleGFtcGxlLCBhIGRpZmZlcmVudCBjb2xvdXIgYmFyIGZvciBlYWNoIHNhbXBsZS4gCgpJZiB3ZSBsb29rIGF0IHRoZSBgZ2VvbV9ib3hwbG90YCBoZWxwIHdlIGNhbiBzZWUgdW5kZXIgdGhlIGhlYWRpbmcgY2FsbGVkICJBZXN0aGV0aWNzIiB0aGF0IHRoZXJlJ3MgYW4gb3B0aW9uIGZvciBjb2xvdXIuIExldCdzIHRyeSBhZGRpbmcgdGhhdCB0byBvdXIgcGxvdC4gV2UnbGwgc3BlY2lmeSB3ZSB3YW50IHRvIG1hcCB0aGUgU2FtcGxlIGNvbHVtbiB0byBgY29sb3VyPWAuIEFzIHdlIGFyZSBtYXBwaW5nIGNvbG91ciB0byBhIGNvbHVtbiBpbiBvdXIgZGF0YSB3ZSBuZWVkIHRvIHB1dCB0aGlzIGluc2lkZSB0aGUgYGFlcygpYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1hbGxpbmZvLCBtYXBwaW5nPWFlcyh4PVNhbXBsZSwgeT1sb2cyKENvdW50KSwgY29sb3VyPVNhbXBsZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpIbW0gY29sb3VyaW5nIHRoZSBlZGdlcyB3YXNu4oCZdCBxdWl0ZSB3aGF0IHdlIGhhZCBpbiBtaW5kLiBMb29rIGF0IHRoZSBoZWxwIGZvciBgZ2VvbV9ib3hwbG90YCB0byBzZWUgd2hhdCBvdGhlciBhZXN0aGV0aWMgd2UgY291bGQgdXNlLiBMZXQncyB0cnkgYGZpbGw9YCBpbnN0ZWFkLgoKYGBge3J9CmdncGxvdChkYXRhPWFsbGluZm8sIG1hcHBpbmc9YWVzKHg9U2FtcGxlLCB5PWxvZzIoQ291bnQpLCBmaWxsPVNhbXBsZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpUaGF0IGxvb2tzIGJldHRlci4gYGZpbGw9YCBpcyB1c2VkIHRvICoqZmlsbCoqIGluIGFyZWFzIGluIGdncGxvdDIgcGxvdHMsIHdoZXJlYXMgYGNvbG91cj1gIGlzIHVzZWQgdG8gY29sb3VyIGxpbmVzIGFuZCBwb2ludHMuCgpBIHJlYWxseSBuaWNlIGZlYXR1cmUgYWJvdXQgZ2dwbG90IGlzIHRoYXQgd2UgY2FuIGVhc2lseSBjb2xvdXIgYnkgYW5vdGhlciB2YXJpYWJsZSBlLmcuIGNlbGwgdHlwZSAoYmFzYWwgdnMgbHVtaW5hbCkgYnkgc2ltcGx5IGNoYW5naW5nIHRoZSBjb2x1bW4gd2UgZ2l2ZSB0byBgZmlsbD1gLgoKIyMjIyBFeGVyY2lzZSAKQ29sb3VyIHRoZSBwbG90cyBieSBvdGhlciB2YXJpYWJsZXMgKGNvbHVtbnMpIGluIHRoZSBtZXRhZGF0YSBmaWxlOgoKMS4gY2hhcmFjdGVyaXN0aWNzCjIuIGltbXVub3BoZW5vdHlwZQozLiBgZGV2ZWxvcG1lbnRhbCBzdGFnZWAgKENoZWNrIHdoYXQgaGFwcGVucyBpZiB5b3UgZG9uJ3QgdXNlIGJhY2t0aWNrcykKCgojIFNwZWNpZnlpbmcgY29sb3VycwoKV2UgbWlnaHQgd2FudCB0byBjaGFuZ2UgdGhlIGNvbG91cnMuIFRvIHNlZSB3aGF0IGNvbG91ciBuYW1lcyBhcmUgYXZhaWxhYmxlIHlvdSBjYW4gdHlwZSBgY29sb3VycygpYC4gTm90ZSB0aGF0IGhlcmUgd2Ugc2VlIHRoZSBmdW5jdGlvbiBgYygpYCBmb3IgdGhlIGZpcnN0IHRpbWUuIFdlIHVzZSBmdW5jdGlvbiBleHRyZW1lbHkgb2Z0ZW4gaW4gUiB3aGVuIHdlIGhhdmUgbXVsdGlwbGUgaXRlbXMgdGhhdCB3ZSBhcmUgKmNvbWJpbmluZyouIEhlcmUgd2UgaGF2ZSB0d28gY29sb3VycyB3ZSB3YW50IHRvIHVzZSwgc28gd2UgbmVlZCB0byB1c2UgYGMoKWAgdG8gY29tYmluZSB0aGVtIHRvIGdpdmUgdG8gYHZhbHVlcz1gLgpgYGB7cn0KbXljb2xvdXJzIDwtIGMoInR1cnF1b2lzZSIsICJwbHVtIiwgInNub3ciLCAibWlzdHlyb3NlIiwgImxlbW9uY2hpZmZvbiIsICJjaG9jb2xhdGUiKQpgYGAKClRoZW4gd2UgZ2l2ZSB0aGVzZSBjb2xvdXJzIHRvICBgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvdXJzKWAuCgpgYGB7cn0KZ2dwbG90KGRhdGE9YWxsaW5mbywgbWFwcGluZz1hZXMoeD1TYW1wbGUsIHk9bG9nMihDb3VudCksIGZpbGw9Y2hhcmFjdGVyaXN0aWNzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3VycykKYGBgCgpUaGVyZSBhcmUgYnVpbHQtaW4gY29sb3VyIHBhbGV0dGVzIHRoYXQgY2FuIGJlIGhhbmR5IHRvIHVzZSwgd2hlcmUgdGhlIHNldHMgb2YgY29sb3VycyBhcmUgcHJlZGVmaW5lZC4gYHNjYWxlX2ZpbGxfYnJld2VyKClgIGlzIGEgcG9wdWxhciBvbmUgKHRoZXJlIGlzIGFsc28gYHNjYWxlX2NvbG91cl9icmV3ZXIoKWApLiBZb3UgY2FuIHRha2UgYSBsb29rIGF0IHRoZSBoZWxwIGZvciBgc2NhbGVfZmlsbF9icmV3ZXIoKWAgdG8gc2VlIHdoYXQgcGFsbGV0dGVzIGFyZSBhdmFpbGFibGUuIFRoZXJlIGlzIGFsc28gYW4gUiBjb2xvdXJzIGNoZWF0c2hlZXQgdGhhdCBzaG93cyB0aGUgY29sb3VycyBvZiB0aGUgcGFsZXR0ZXMgW2hlcmVdKGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L35mcmF6aWVyL1JTcGF0aWFsR3VpZGVzL2NvbG9yUGFsZXR0ZUNoZWF0c2hlZXQucGRmKS4gVGhlcmUncyBvbmUgY2FsbGVkICJEYXJrMiIsIGxldCdzIGhhdmUgYSBsb29rIGF0IHRoYXQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9YWxsaW5mbywgbWFwcGluZz1hZXMoeD1TYW1wbGUsIHk9bG9nMihDb3VudCksIGZpbGw9Y2hhcmFjdGVyaXN0aWNzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKCiMjIyMgRXhlcmNpc2UKTWFrZSBhIGNvbG91cmJsaW5kIGZyaWVuZGx5IHBsb3QuIEhpbnQgdGhlcmUgYXJlIGNvbG91cmJsaW5kIGZyaWVuZGx5IHBhbGV0dGVzIFtoZXJlXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9Db2xvcnNfKGdncGxvdDIpLyNhLWNvbG9yYmxpbmQtZnJpZW5kbHktcGFsZXR0ZSkKCiMgTWFrZSBzdWJwbG90cyBmb3IgZWFjaCBnZW5lCgpXaXRoIGdncGxvdCB3ZSBjYW4gZWFzaWx5IG1ha2Ugc3VicGxvdHMgdXNpbmcgKmZhY2V0aW5nKi4gRm9yIGV4YW1wbGUgd2UgY2FuIG1ha2Ugc3RyaXBjaGFydHMsIHBsb3R0aW5nIGV4cHJlc3Npb24gYnkgZ3JvdXBzIGZvciBlYWNoIGdlbmUuIEZpcnN0IHdlJ2xsIHVzZSBgbXV0YXRlYCB0byBhZGQgYSBjb2x1bW4gd2l0aCBzaG9ydGVyIGdyb3VwIG5hbWVzIHRvIHVzZSBpbiB0aGUgcGxvdCBhcyB0aGUgZ3JvdXAgbmFtZXMgaW4gdGhlIGNoYXJhY3RlcmlzdGljcyBjb2x1bW4gYXJlIHF1aXRlIGxvbmcuCgpgYGB7cn0KIGFsbGluZm8gPC0gbXV0YXRlKGFsbGluZm8sIEdyb3VwPWNhc2Vfd2hlbiggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgc3RyX2RldGVjdChjaGFyYWN0ZXJpc3RpY3MsICJiYXNhbC4qdmlyZ2luIikgfiAiYnZpcmciLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAiYmFzYWwuKnByZWciKSAgfiAiYnByZWciLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAiYmFzYWwuKmxhY3QiKSAgfiAiYmxhY3QiLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAibHVtaW5hbC4qdmlyZ2luIikgIH4gImx2aXJnIiwKICAgICAgICBzdHJfZGV0ZWN0KGNoYXJhY3RlcmlzdGljcywgImx1bWluYWwuKnByZWciKSAgfiAibHByZWciLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAibHVtaW5hbC4qbGFjdCIpICB+ICJsbGFjdCIKICAgICAgICkpCmBgYAoKV2UgY2FuIG1ha2UgcGxvdHMgZm9yIGEgc2V0IG9mIGdlbmVzLgoKYGBge3J9Cm15Z2VuZXMgPC0gYygiQWN0YTIiLCAiQ09YMSIsICJDc24xczEiLCAiQ3NuMXMyYSIsICJDc24yIiwgIkNzbjMiKQpgYGAKCgpUaGVzZSBhcmUgdGhlIHRvcCA2IGdlbmVzIHdpdGggaGlnaGVzdCBjb3VudCBhY3Jvc3MgYWxsIHNhbXBsZXMgdGhhdCBjYW4gYmUgb2J0YWluZWQgd2l0aCBncm91cF9ieSBhbmQgc3VtbWFyaXNlCm15Z2VuZXMgPC0gYWxsaW5mbyAlPiUgCmdyb3VwX2J5KGdlbmVfc3ltYm9sKSAlPiUgCnN1bW1hcmlzZShUb3RhbF9jb3VudD1zdW0oQ291bnQpKSAlPiUgCnRvcF9uKDYsIFRvdGFsX2NvdW50KSU+JQpwdWxsKGdlbmVfc3ltYm9sKQoKV2UgZmlsdGVyIG91ciBkYXRhIGZvciBqdXN0IHRoZXNlIGdlbmVzIG9mIGludGVyZXN0LgoKYGBge3J9Cm15Z2VuZXNfY291bnRzIDwtIGZpbHRlcihhbGxpbmZvLCBnZW5lX3N5bWJvbCAlaW4lIG15Z2VuZXMpCmBgYAoKCldlIGNhbiBtYWtlIGJveHBsb3RzIGZvciBqdXN0IHRoZXNlIGdlbmVzLiBXZSAqZmFjZXQqIG9uIHRoZSBnZW5lX3N5bWJvbCBjb2x1bW4uCgpgYGB7cn0KZ2dwbG90KGRhdGE9bXlnZW5lc19jb3VudHMsIG1hcHBpbmc9YWVzKHg9R3JvdXAsIHk9Q291bnQsIGZpbGw9R3JvdXApKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGZhY2V0X3dyYXAofmdlbmVfc3ltYm9sKQpgYGAKV2Ugb25seSBoYXZlIHR3byB2YWx1ZXMgcGVyIGdyb3VwIHNvIHdlIGNvdWxkIGp1c3QgcGxvdCB0aGUgaW5kaXZpZHVhbCBwb2ludHMuIFdlIGNvdWxkIHVzZSAKZ2VvbV9wb2ludCB0byBtYWtlIGEgc2NhdHRlcnBsb3QuCmBgYHtyfQpnZ3Bsb3QoZGF0YT1teWdlbmVzX2NvdW50cywgbWFwcGluZz1hZXMoeD1Hcm91cCwgeT1Db3VudCkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAofmdlbmVfc3ltYm9sKQpgYGAKVGhlIHBvaW50cyBhcmUgb3ZlcmxhcHBpbmcgc28gd2Ugd2lsbCB1c2UgZ2VvbV9qaXR0ZXIgd2hpY2ggYWRkcyBhIHNtYWxsIGFtb3VudCBvZiByYW5kb20gdmFyaWF0aW9uLgoKYGBge3J9CmdncGxvdChkYXRhPW15Z2VuZXNfY291bnRzLCBtYXBwaW5nPWFlcyh4PUdyb3VwLCB5PUNvdW50KSkgKwogIGdlb21faml0dGVyKCkgKwogIGZhY2V0X3dyYXAofmdlbmVfc3ltYm9sKQpgYGAKCldlIGNhbiBjb2xvdXIgdGhlIGdyb3VwcyBzaW1pbGFyIHRvIGJlZm9yZSB1c2luZyBgY29sb3VyPWAuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bXlnZW5lc19jb3VudHMsIG1hcHBpbmc9YWVzKHg9R3JvdXAsIHk9Q291bnQsIGNvbG91cj1Hcm91cCkpICsKICBnZW9tX2ppdHRlcigpICsKICBmYWNldF93cmFwKH5nZW5lX3N5bWJvbCkgCmBgYAoKIyMgTW9kaWZ5aW5nIHRoZSBwbG90CgojIyMgQXhpcyBsYWJlbHMgYW5kIFRpdGxlCgpXZSBjYW4gY2hhbmdlIHRoZSBheGlzIGxhYmVscyBhbmQgYWRkIGEgdGl0bGUgd2l0aCBgbGFicygpYC4gVG8gY2hhbmdlIHRoZSB4IGF4aXMgbGFiZWwgd2UgdXNlIGBsYWJzKHg9Ik5ldyBuYW1lIilgLiBUbyBjaGFuZ2UgdGhlIHkgYXhpcyBsYWJlbCB3ZSB1c2UgYGxhYnMoeT0iTmV3IG5hbWUiKWAgb3Igd2UgY2FuIGNoYW5nZSB0aGVtIGFsbCBhdCB0aGUgc2FtZSB0aW1lLgoKYGBge3J9CmdncGxvdChkYXRhPW15Z2VuZXNfY291bnRzLCBtYXBwaW5nPWFlcyh4PUdyb3VwLCB5PUNvdW50LCBjb2xvdXI9R3JvdXApKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZmFjZXRfd3JhcCh+Z2VuZV9zeW1ib2wpICsgCiAgbGFicyh4PSJDZWxsIHR5cGUgYW5kIHN0YWdlIiwgeT0iQ291bnQiLCB0aXRsZT0iTWFtbWFyeSBnbGFuZCBSTkEtc2VxIGRhdGEiKQpgYGAKCiMjIyBUaGVtZXMKCldlIGNhbiBhZGp1c3QgdGhlIHRleHQgb24gdGhlIHggYXhpcyAodGhlIGdyb3VwIGxhYmVscyBieSB0dXJuaW5nIHRoZW0gOTAgZGVncmVlcyBzbyB3ZSBjYW4gcmVhZCB0aGUgbGFiZWxzIGJldHRlci4KYGBge3J9CmdncGxvdChkYXRhPW15Z2VuZXNfY291bnRzLCBtYXBwaW5nPWFlcyh4PUdyb3VwLCB5PUNvdW50LCBjb2xvdXI9R3JvdXApKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZmFjZXRfd3JhcCh+Z2VuZV9zeW1ib2wpICsgCiAgbGFicyh4PSJDZWxsIHR5cGUgYW5kIHN0YWdlIiwgeT0iQ291bnQiLCB0aXRsZT0iTWFtbWFyeSBnbGFuZCBSTkEtc2VxIGRhdGEiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKV2UgY2FuIHJlbW92ZSB0aGUgZ3JleSBiYWNrZ3JvdW5kIGFuZCBncmlkIGxpbmVzLiBUbyBkbyB0aGlzIHdlIG1vZGlmeSB0aGUgZ2dwbG90IHRoZW1lLiBUaGVtZXMgYXJlIHRoZSBub24tZGF0YSBwYXJ0cyBvZiB0aGUgcGxvdC4gCgpUaGVyZSBhcmUgYWxzbyBhIGxvdCBvZiBidWlsdC1pbiB0aGVtZXMuIExldCdzIGhhdmUgYSBsb29rIGF0IGEgY291cGxlIG9mIHRoZSBtb3JlIHdpZGVseSB1c2VkIHRoZW1lcy4gV2Ugd29uJ3Qgc2F2ZSB0aGVzZSAod2Ugd29uJ3QgdXNlIGBwIDwtYCkgd2UnbGwganVzdCBwcmludCB0aGVtIHRvIGhhdmUgYSBsb29rLiBUaGUgZGVmYXVsdCBnZ3Bsb3QgdGhlbWUgaXMgYHRoZW1lX2dyZXkoKS5gCgpgYGB7cn0KZ2dwbG90KGRhdGE9bXlnZW5lc19jb3VudHMsIG1hcHBpbmc9YWVzKHg9R3JvdXAsIHk9Q291bnQsIGNvbG91cj1Hcm91cCkpICsKICBnZW9tX2ppdHRlcigpICsKICBmYWNldF93cmFwKH5nZW5lX3N5bWJvbCkgKyAKICBsYWJzKHg9IkNlbGwgdHlwZSBhbmQgc3RhZ2UiLCB5PSJDb3VudCIsIHRpdGxlPSJNYW1tYXJ5IGdsYW5kIFJOQS1zZXEgZGF0YSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKyAKICB0aGVtZV9idygpCmBgYAoKYGBge3J9CmdncGxvdChkYXRhPW15Z2VuZXNfY291bnRzLCBtYXBwaW5nPWFlcyh4PUdyb3VwLCB5PUNvdW50LCBjb2xvdXI9R3JvdXApKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZmFjZXRfd3JhcCh+Z2VuZV9zeW1ib2wpICsgCiAgbGFicyh4PSJDZWxsIHR5cGUgYW5kIHN0YWdlIiwgeT0iQ291bnQiLCB0aXRsZT0iTWFtbWFyeSBnbGFuZCBSTkEtc2VxIGRhdGEiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsgCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlcmUgYXJlIG1hbnkgdGhlbWVzIGF2YWlsYWJsZSwgeW91IGNhbiBzZWUgc29tZSBpbiB0aGUgW1IgZ3JhcGggZ2FsbGVyeV0oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8xOTItZ2dwbG90LXRoZW1lcy8pLgoKV2UgY2FuIGFsc28gbW9kaWZ5IHBhcnRzIG9mIHRoZSB0aGVtZSBpbmRpdmlkdWFsbHkuIFdlIGNhbiByZW1vdmUgdGhlIGdyZXkgYmFja2dyb3VuZCBhbmQgZ3JpZCBsaW5lcyB3aXRoIHRoZSBjb2RlIGJlbG93LgoKYGBge3J9CmdncGxvdChkYXRhPW15Z2VuZXNfY291bnRzLCBtYXBwaW5nPWFlcyh4PUdyb3VwLCB5PUNvdW50LCBjb2xvdXI9R3JvdXApKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZmFjZXRfd3JhcCh+Z2VuZV9zeW1ib2wpICsgCiAgbGFicyh4PSJDZWxsIHR5cGUgYW5kIHN0YWdlIiwgeT0iQ291bnQiLCB0aXRsZT0iTWFtbWFyeSBnbGFuZCBSTkEtc2VxIGRhdGEiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyBTYXZpbmcgcGxvdHMKV2UgY2FuIHNhdmUgcGxvdHMgaW50ZXJhY3RpdmVseSBieSBjbGlja2luZyBFeHBvcnQgaW4gdGhlIFBsb3RzIHdpbmRvdy4gT3Igd2UgY2FuIG91dHB1dCBwbG90cyB0byBwZGYgdXNpbmcgYHBkZigpYCBmb2xsb3dlZCBieSBgZGV2Lm9mZigpYC4gV2UgcHV0IG91ciBwbG90IGNvZGUgYWZ0ZXIgdGhlIGNhbGwgdG8gYHBkZigpYCBhbmQgYmVmb3JlIGNsb3NpbmcgdGhlIHBsb3QgZGV2aWNlIHdpdGggYGRldi5vZmYoKWAuCgpMZXQncyBzYXZlIG91ciBsYXN0IHBsb3QuCgpgYGB7ciwgZXZhbD1GQUxTRX0KcGRmKCJteXBsb3QucGRmIikKZ2dwbG90KGRhdGE9bXlnZW5lc19jb3VudHMsIG1hcHBpbmc9YWVzKHg9R3JvdXAsIHk9Q291bnQsIGNvbG91cj1Hcm91cCkpICsKICBnZW9tX2ppdHRlcigpICsKICBmYWNldF93cmFwKH5nZW5lX3N5bWJvbCkgKyAKICBsYWJzKHg9IkNlbGwgdHlwZSBhbmQgc3RhZ2UiLCB5PSJDb3VudCIsIHRpdGxlPSJNYW1tYXJ5IGdsYW5kIFJOQS1zZXEgZGF0YSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpkZXYub2ZmKCkKYGBgCgoKIyMgS2V5IFBvaW50cwotIFdlIHVzZSB0aGUgYGxpYnJhcnkoKWAgZnVuY3Rpb24gdG8gbG9hZCBwYWNrYWdlcyB3ZSB3YW50IHRvIHVzZS4gTm90ZSB0aGF0IHRoZXkgbmVlZCB0byBiZSBhbHJlYWR5IGluc3RhbGxlZCB3aXRoIGUuZy4gYGluc3RhbGwucGFja2FnZXMoKWAuIAotIFdlIGNhbiByZWFkIGluIGNzdiBmaWxlcyB3aXRoIGByZWFkX2NzdigpYC4KLSAqKmBnZ3Bsb3QyYCoqIGlzIGEgcGxvdHRpbmcgcGFja2FnZSB0aGF0IG1ha2VzIGl0IHNpbXBsZSB0byBjcmVhdGUgY29tcGxleCBwbG90cyBhbmQgaGF2ZSAzIGNvbXBvbmVudHM6IGRhdGEgKGRhdGFzZXQpLCBtYXBwaW5nIChjb2x1bW5zIHRvIHBsb3QpIGFuZCBnZW9tICh0eXBlIG9mIHBsb3QpLgotIFRoZSBgK2Agc2lnbiBpcyB1c2VkIHRvIGFkZCBnZW9tcyAoYW5kIG5ldyBsYXllcnMgYXMgd2Ugd2lsbCBzZWUgbGF0ZXIgaW4gdGhlIGNvdXJzZSkuIEl0IG11c3QgYmUgcGxhY2VkIGF0IHRoZSBlbmQgb2YgdGhlIHByZWNlZGluZyBsaW5lLiBJZiBpdCBpcyBhZGRlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBsaW5lLCAqKmBnZ3Bsb3QyYCoqIHdpbGwgcmV0dXJuIGFuIGVycm9yIG1lc3NhZ2UuCi0gZ2dwbG90MiBwbG90cyBjYW4gYmUgZWFzaWx5IGNvbG91cmVkIGJ5IGNvbHVtbnMgaW4gdGhlIGRhdGFzZXQuIGBmaWxsPWAgY2FuIGJlIHVzZWQgdG8gY29sb3VyIGFyZWFzLgotIEEgcGRmIG9mIHRoZSBwbG90IGNhbiBiZSBnZW5lcmF0ZWQgd2l0aCBgcGRmKClgIGFuZCBgZGV2Lm9mZigpYAoKCiMjIEZ1cnRoZXIgUmVhZGluZwpbSW50cm8gdG8gUiBhbmQgdGlkeXZlcnNlXShodHRwczovL3BtYWNkYXNjaS5naXRodWIuaW8vci1pbnRyby10aWR5dmVyc2UvKQpbVG9wIDUwIEdncGxvdCBWaXN1YWxpc2F0aW9uc10oIGh0dHA6Ly9yLXN0YXRpc3RpY3MuY28vVG9wNTAtR2dwbG90Mi1WaXN1YWxpemF0aW9ucy1NYXN0ZXJMaXN0LVItQ29kZS5odG1sKQpbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykK